package ws.jdbc.concurrent.driver.sqlserver.tds;

import java.util.List;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import ws.jdbc.concurrent.driver.sqlserver.tds.datastreamtypes.B_VARCHAR;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_INFO;

/**
 * Handles the {@code COLMETADATA} token reading process.
 *
 * <p>
 * The {@code COLMETADATA} describes the result set for interpretation
 * of following {@code ROW} datastreams.
 * </p>
 *
 * <p>
 * This token stream used to tell the client the data type and length of the column
 * data. It describes the format of the data found in a {@code ROW} data stream.
 * </p>
 *
 * <p>
 * All {@code COLMETADATA} data streams are grouped together.
 * </p>
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class ColMetadataTokenReader extends TokenReader {

  /**
   * The count of columns (number of aggregate operators) in the token stream.
   * <p>
   * In the event that the client requested no metadata to be returned
   * (see OptionFlags parameter in RPCRequest token), the value of Count will be 0xFFFF.
   * This has the same effect on Count as a zero value (for example, no ColumnData stream sent).
   * </p>
   */
  private short count;
  /**
   * List with the information for all the column data found on the token.
   */
  private List<ColumnData> columnList;

  /**
   * Create a new reader instance.
   *
   * @param stream data input stream to read the token from.
   */
  ColMetadataTokenReader(final ByteBuffer stream) {
    super(stream);
  }

  /**
   * Read one {@code COLMETADATA} token.
   *
   * @return number of bytes read.
   *
   * @throws IOException if an error occurs while reading from the data input stream.
   */
  @Override
  int read() throws IOException {
    int bytesRead = 0;

    // Token definition: NoMetaData / (Count *<Count>ColumnData)
    //  Count
    count = stream.getShort();
    bytesRead += 2;
    if (count == (short) 0xFFFF) {
      // NoMetaData
      bytesRead = 2;
    } else {
      // create list with 'count' elements
      columnList = new ArrayList<ColumnData>(count);

      for (int i = 0; i < count; ++i) {
        // ColumnData
        //  UserType (USHORT/ULONG; (Changed to ULONG in TDS 7.2))
        int userType = stream.getInt();
        bytesRead += 4;
        //  Flags
        short flags = stream.getShort();
        bytesRead += 2;
        //  TYPE_INFO
        TYPE_INFO typeInfo = dtddsReader.readTypeInfo();

        //  [TableName]
        //  This exists only for text, ntext and image columns.
        //  And since this data are not yet supported, this field will be ignored.

        //  ColName
        B_VARCHAR colName = vldsReader.readBVarChar();

        /* Add column to the list. */
        ColumnData col = new ColumnData(userType, flags, typeInfo, colName);
        columnList.add(col);
      }
    }
    return bytesRead + dtddsReader.getBytesRead() + vldsReader.getBytesRead();
  }

  public List<ColumnData> getColumnsMetaData() {
    return columnList;
  }

  /**
   * Get column name.
   *
   * @param columnIndex column number whose name will be retrieved.
   *
   * @return column name.
   */
  String getColumnName(int columnIndex) {
    return columnList.get(columnIndex).
            getColName();
  }

  TYPE_INFO getColumnInfo(int columnIndex) {
    return columnList.get(columnIndex).
            getTypeInfo();
  }

  /**
   * Token as a {@code String}.
   *
   * @return token fields as a {@code String}.
   */
  @Override
  public String toString() {
    String ret = "ColMetadataTokenReader{";
    for (ColumnData col : columnList) {
      ret += "\n" + col.toString();
    }
    return ret + "\n}";
  }
}
